#include <stdio.h>
#include <stdlib.h>
#include "timers.h"
#include "mpi.h"

//czas
void allocateMatrix(int **matr, int hei_a, int wid_a, int randBool);
void allocateMatrix1D(int *matr, int hei_a, int wid_a, int randBool);
void malMatrix(int *a, int *b, int wid_a, int hei_a, int wid_b, int *res);
void printMatrix(int *tab, int wid, int hei);
long int recTimer();

MPI_Request request;
MPI_Status status;

main(int argc, char **argv)
{
	long int time_start = recTimer();
	int np, rank, wid_a, wid_b, hei_a, hei_b;

	if(argc==4) {
		wid_a = (int) strtol(argv[1], NULL, 10);
		hei_a = (int) strtol(argv[2], NULL, 10);
		wid_b = (int) strtol(argv[3], NULL, 10);

	} else {
		wid_a = 10;
		hei_a = 10;
		wid_b = 2;

	}
	hei_b = wid_a;

	MPI_Status status;

	MPI_Init(&argc, &argv);
	MPI_Comm_rank(MPI_COMM_WORLD, &rank);
	MPI_Comm_size(MPI_COMM_WORLD, &np);

	int roundUp = hei_a/np;

	int *matrix_a = malloc(sizeof *matrix_a * hei_a * wid_a);
	int *matrix_b = malloc(sizeof *matrix_b * hei_b * wid_b);
	int *matrix_res = malloc(sizeof *matrix_res * hei_a * wid_b);
	int *matrix_res_div = malloc(sizeof *matrix_res * roundUp * wid_b);
	int *matrix_a_div = malloc(sizeof *matrix_a_div * roundUp * wid_a);

	unsigned int iseed = (unsigned int)time(NULL);
	srand (iseed);

	int i=0;
	int l;

	int test = 0;

	allocateMatrix1D(matrix_a_div, roundUp, wid_a, 0);
	allocateMatrix1D(matrix_res_div, roundUp, wid_b, 0);

	if(rank==0) {

		allocateMatrix1D(matrix_a, hei_a, wid_a, 1);
		allocateMatrix1D(matrix_b, hei_b, wid_b, 1);
		allocateMatrix1D(matrix_res, hei_a, wid_b, 0);

	}

	MPI_Bcast( matrix_b, (wid_b * hei_b), MPI_INT, 0, MPI_COMM_WORLD);

	MPI_Scatter(matrix_a, (roundUp*wid_a), MPI_INT, matrix_a_div , (roundUp*wid_a), MPI_INT, 0, MPI_COMM_WORLD);
	
	long int time_process = recTimer();
	
	malMatrix(matrix_a_div, matrix_b, wid_a, roundUp, wid_b, matrix_res_div);
	
	printf("Czas pojedynczego procesu: %0.10f \n", (double)(recTimer()-time_process) / 1000000000);
	
	MPI_Gather(matrix_res_div, (roundUp*wid_b), MPI_INT, matrix_res , (roundUp*wid_b), MPI_INT, 0, MPI_COMM_WORLD);
	
	if(rank==0) {
		if(wid_a < 6 && wid_b < 6) {
		printMatrix(matrix_a, wid_a, hei_a);
		printf(" \n ------------------*----------------------- \n");
		printMatrix(matrix_b, wid_b, hei_b);
		printf(" \n -------------------=----------------------- \n");
		printMatrix(matrix_res, wid_b, hei_a);
		}
	}

	MPI_Finalize();
	if(rank==0) {
		printf("Czas całego programu: %f \n", (double)(recTimer()-time_start) / 1000000000);
	}
}

void allocateMatrix(int **matr, int hei_a, int wid_a, int randBool) {
	if (matr) {
		int i, j;

		for (i = 0; i < hei_a; i++) {
			matr[i] = malloc(sizeof(*matr[i]) * wid_a);

			for (j = 0; j < wid_a; j++) {
				if (randBool) {
					matr[i][j] = rand() / 10000000;
				} else {
					matr[i][j] = 0;
				}
			}

		}
	}

}

void allocateMatrix1D(int *matr, int hei_a, int wid_a, int randBool) {
	if (matr) {
		int i, j;

		for (i = 0; i < hei_a; i++) {

			for (j = 0; j < wid_a; j++) {
				if (randBool) {
					matr[(i * wid_a) + j] = rand() / 100000000;
				} else {
					matr[(i * wid_a) + j] = 0;
				}
			}

		}
	}

}

/*void malMatrix(int **a, int **b, int wid_a, int hei_a, int wid_b, int **res) {
 int i, j, k;
 for (i = 0; i < hei_a; i++) {
 for (j = 0; j < wid_b; j++) {
 for (k = 0; k < wid_a; k++) {

 res[i][j] = res[i][j] + (a[i][k] * b[k][j]);
 }
 }
 }

 }*/
void malMatrix(int *a, int *b, int wid_a, int hei_a, int wid_b, int *res) {
	int i, j, k;
	for (i = 0; i < hei_a; i++) {
		for (j = 0; j < wid_b; j++) {
			for (k = 0; k < wid_a; k++) {

				res[(i * wid_b) + j] = res[(i * wid_b) + j]
						+ (a[(i * wid_a) + k] * b[(k * wid_b) + j]);
			}
		}
	}

}

void printMatrix(int *tab, int wid, int hei) {
	int i, j;
	for (i = 0; i < hei; i++) {
		for (j = 0; j < wid; j++) {
			printf(" %12d ", tab[(i * wid) + j]);
		}
		printf("\n");
	}

}
long int recTimer() {
	struct timespec t;
	clock_gettime(CLOCK_MONOTONIC, &t);
	return ((t.tv_sec * TIMER_SCALE) + t.tv_nsec);
}
